#pragma once

#include "log.hpp"
#include<iostream>
#include<cstdio>
#include<string>
#include<cerrno>
#include<cstring>
#include<cstdlib>
#include<unistd.h>
#include<unordered_map>
#include<queue>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>

#define SIZE 1024

class UdpServer
{
public:
    // 绑定任意ip后, ip参数可以不写
    UdpServer(uint16_t port, std::string ip = ""):_port(port),_ip(ip),_sock(-1)
    {}

    // 这里是系统调用，来完成网络功能
    bool initServer()
    {
        // 1. 创建套接字

        _sock = socket(AF_INET, SOCK_DGRAM, 0);//网络通信以数据报的形式
        if(_sock < 0)
        {
            logMessage(FATAL, "%d:%s", errno, strerror(errno));
            exit(2);
        }

        // 2. bind:将用户设置的ip和port在内核中和我们当前的进程强关联
        struct sockaddr_in local; // 里面需要填写三个参数 - 地址类型, 端口号, IP地址
        bzero(&local, 0); // 初始化
        local.sin_family = AF_INET; // (socket中是网络通信)结构体对象将会与网络服务器绑定
        local.sin_port = htons(_port); // 主机序列转网络序列
        //【服务器建议绑定任意ip】让服务器在工作过程中，可以从任意IP中获取数据
        local.sin_addr.s_addr = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str()); // INADDR_ANY:宏就是0

        if(bind(_sock,(struct sockaddr*)&local, sizeof(local)) < 0)
        {
            logMessage(FATAL, "%d:%s", errno, strerror(errno));
            exit(2);  
        }
        logMessage(NORMAL, "init udp server done ... %s", strerror(errno));
        return true;
    }
    void start()
    {
        // 作为一款网络服务器，永远不退出的！
         char buffer[SIZE];
         for(;;)
         {          
            struct sockaddr_in peer; // peer,纯输出型参数
            bzero(&peer, sizeof(peer));
            socklen_t len = sizeof(peer); // 输入输出性参数
            ssize_t s = recvfrom(_sock, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len); 
            
            char result[256];
            char key[64];

            if(s > 0)
            {
                // 【发生成功进来】
                buffer[s] = 0; // 读入结尾没有\0,需要自己加将数据当作一个字符串
                // 将对方的ip和port填入peer
                uint16_t cli_port = ntohs(peer.sin_port);      // 从网络中来的！-- 需要网络转主机
                std::string cli_ip = inet_ntoa(peer.sin_addr); // 4字节的网络序列的IP -转- 本主机的字符串风格的IP，方便显示
                snprintf(key, sizeof(key), "%s-%u", cli_ip.c_str(), cli_port); // 127.0.0.1-8080
                logMessage(NORMAL, "key: %s", key);
                
                auto it = _users.find(key);
                if (it == _users.end())
                {
                    // exists
                    logMessage(NORMAL, "add new user : %s", key);
                    _users.insert({key, peer});
                }
            }
            // 当有A B C三个客户端都发过消息给服务器, 服务器会将A B C的ip和port存起来，当A发消息给服务器时，服务器会将消息发给A B C三个客户端，而不再只是发给A
            for (auto &iter : _users)
            {
                std::string sendMessage = key; // 这里要注意是前面key而不是 iter.first 中key
                sendMessage += "# ";
                sendMessage += buffer; // 127.0.0.1-1234# 你好
                logMessage(NORMAL, "push message to %s", iter.first.c_str());
                sendto(_sock, sendMessage.c_str(), sendMessage.size(), 0, (struct sockaddr *)&(iter.second), sizeof(iter.second));
            }
         }

    }
    ~UdpServer()
    {
        if(_sock >= 0)
            close(_sock);
    }
private:
    // 一个服务器必须要有ip地址和port(16位的整数)
    std::string _ip;
    uint16_t _port;
    int _sock;
    std::unordered_map<std::string, struct sockaddr_in> _users;
    std::queue<std::string> messageQueue;
};


